﻿//-----------------------------------------------------------------------
// <copyright file="TupleList.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the TupleList class.</summary>
//-----------------------------------------------------------------------

// #define BIND_FUNCTION_PERF
namespace Tulip.Engine.Tuples
{
    using Exceptions;

    /// <summary>
    /// Delegate type for callback function.   This follows the monadic pattern by
    /// mapping an inner value (a list of tuples) to a TupleList monad.
    /// </summary>
    /// <param name="tuples">A list of tuple monads.</param>
    /// <returns>A TupleList Monad.</returns>
    public delegate TupleList MonadicFunction(TupleMonadLinkedList tuples);

    /// <summary>
    /// Monadic class that represents a list of Tuples and supports 'single mode' 
    /// in which the list contains a single tuple.
    /// </summary>
    /// <remarks>
    /// This class, despite its brevity, forms the heart of the engine.   It implements
    /// the monadic pattern commonly used in functional languages and based on the concept
    /// of monads in category theory.  A monad is a monoidal endofunctor (i.e., it maps 
    /// objects from a category onto the same category).   Its job is to provide the 
    /// essential mechanism for implementing functional modularity.  In this case, the 
    /// monad composes functions (generated per rule set) into computational sequences.   
    /// Those functions are responsible for evaluation, joining, etc., and exhibit side 
    /// effects by operating on the tupleStore.   One of the tasks of monads is to hide and 
    /// manage side effects, allowing the pure functional paradigm to be maintained across 
    /// sequences of function evaluations.
    /// </remarks>
    public class TupleList
    {
#if BIND_FUNCTION_PERF
        public static Dictionary<string, TimeSpan> BindFuncPerf = new Dictionary<string, TimeSpan>();
#endif
        /// <summary>
        /// The monad's value.   The monad represents a list of tuples.
        /// </summary>
        private readonly TupleMonadLinkedList innerList = new TupleMonadLinkedList();

        /// <summary>
        /// Gets a value indicating whether the monad is in 'single tuple' mode.   This is a convenience
        /// feature and allows a small degree of optimisation by removing the need to enumerate lists that 
        /// contain a single tuple.
        /// </summary>
        private readonly bool inSingleMode;

        /// <summary>
        /// Multicast delegate provides an invocation list of the functions to be invoked
        /// by the next invocation of the bind operator.
        /// </summary>
        /// <remarks>
        /// The TupleList monad allows functional sequences to fork into multiple branches
        /// in order to represent trees.  This is enabled by using multicast delegates.
        /// </remarks>
        private MonadicFunction nextFunctions;

        /// <summary>
        /// Multicast delegate provides an invocation list of the quantifiaction functions 
        /// whose execution will be deferred.
        /// </summary>
        /// <remarks>
        /// The TupleList monad allows functional sequences to fork into multiple branches
        /// in order to represent trees.  This is enabled by using multicast delegates.
        /// </remarks>
        private MonadicFunction nextQuantificationFunctions;

        /// <summary>
        /// Reference to the current Tuple session.
        /// </summary>
        private TupleStoreSession session;

        /// <summary>
        /// Specifies that the monad is handling quantification.    Execution must be deferred until
        /// all non-quantifiable monads that can currently be reached have been executed.
        /// </summary>
        private bool quantifiable;

        /// <summary>
        /// Initializes a new instance of the TupleList class.  Serves as an overloaded 'unit' function for the monad.
        /// Constructs a monad over a list containing a single tuple, and switches the 
        /// monad into 'single' mode.
        /// </summary>
        /// <param name="tuple">
        /// Tuple to be held within the monad's value (a list) in single mode.
        /// </param>
        public TupleList(Tuple tuple)
        {
            this.innerList.Add(tuple);
            this.inSingleMode = true;
        }

        /// <summary>
        /// Initializes a new instance of the TupleList class.  Serves as an overloaded type 'unit' function for the monad.
        /// </summary>
        /// <param name="tuples">
        /// A list of tuples.   NB., al tuples will be of the same derived type.
        /// </param>
        public TupleList(TupleMonadLinkedList tuples)
        {
            this.innerList = tuples;
        }

        /// <summary>
        /// Initializes a new instance of the TupleList class.  Serves as an overloaded type 'unit' function for the monad.
        /// Constructs a monad over a list containing a single tuple, and switches the 
        /// monad into 'single' mode.
        /// </summary>
        /// <param name="tuple">
        /// Tuple to be held within the monad's value (a list) in single mode.
        /// </param>
        /// <param name="session">
        /// The session in which the monad lives.   This is treated as 
        /// internal monadic context from this point on.
        /// </param>
        public TupleList(Tuple tuple, TupleStoreSession session)
        {
            this.innerList.Add(tuple);
            this.inSingleMode = true;
            this.session = session;
        }

        /// <summary>
        /// Initializes a new instance of the TupleList class.  Serves as an overloaded type 'unit' function for the monad.
        /// </summary>
        /// <param name="tuples">
        /// A list of tuples.   NB., al tuples will be of the same derived type.
        /// </param>
        /// <param name="session">
        /// The session in which the monad lives.   This is treated as 
        /// internal monadic context from this point on.
        /// </param>
        public TupleList(TupleMonadLinkedList tuples, TupleStoreSession session)
        {
            this.innerList = tuples;
            this.session = session;
        }

        /// <summary>
        /// Gets the first tuple in the list.   This should only be used if the monad
        /// is in single mode.
        /// </summary>
        public Tuple SingleValue
        {
            get
            {
                if (this.inSingleMode)
                {
                    return this.innerList.Count > 0 ? this.innerList.First : null;
                }

                return TupleStoreExceptionHelper.ThrowTupleListSingleModeException();
            }
        }

        /// <summary>
        /// Add a monadic function to invocation list used by the Bind method.
        /// </summary>
        /// <param name="monadicFunction">The monadic function to be added.</param>
        /// <remarks>
        /// The TupleList monad elaborates the basic monadic pattern by supporting branching
        /// in the computational sequence.  To enable this, a monad can be provisioned with
        /// multiple monadic functions which are then invoked in the Bind method.
        /// </remarks>
        public void AddMonadicFunction(MonadicFunction monadicFunction)
        {
            System.Diagnostics.Debug.Assert(this.nextQuantificationFunctions == null || this.nextQuantificationFunctions.GetInvocationList().Length == 0, "The invocation list of the next set of qunatification functions is not empty.");

            if (this.nextFunctions == null)
            {
                this.nextFunctions = monadicFunction;
            }
            else
            {
                this.nextFunctions += monadicFunction;
            }

            this.quantifiable = false;
        }

        /// <summary>
        /// Add a function to the invocation list used by the Bind method.   The invocation list 
        /// is for quantification functions that will have deferred execution.
        /// </summary>
        /// <param name="monadicFunction">The quantification monadic function to be addded.</param>
        /// <remarks>
        /// The TupleList monad elaborates the basic monadic pattern by supporting branching
        /// in the computational sequence.  To enable this, a monad can be provisioned with
        /// multiple monadic functions which are then invoked in the Bind method.
        /// </remarks>
        public void AddQuantificationMonadicFunction(MonadicFunction monadicFunction)
        {
            System.Diagnostics.Debug.Assert(this.nextFunctions == null || this.nextFunctions.GetInvocationList().Length == 0, "The invocation list of the next set of functions is not empty.");

            if (this.nextFunctions == null)
            {
                this.nextQuantificationFunctions = monadicFunction;
            }
            else
            {
                this.nextQuantificationFunctions += monadicFunction;
            }

            this.quantifiable = true;
        }

        /// <summary>
        /// Starts the execution of a computational sequence.
        /// </summary>
        public void ExecuteSequence()
        {
            foreach (MonadicFunction bf in this.nextFunctions.GetInvocationList())
            {
                this.Bind(bf);
            }
        }

        /// <summary>
        /// Starts the execution of a computational sequence.
        /// </summary>
        public void ExecuteSequenceForQuantification()
        {
            foreach (MonadicFunction bf in this.nextQuantificationFunctions.GetInvocationList())
            {
                this.Bind(bf);
            }
        }

        /// <summary>
        /// The Monad's Bind method.   
        /// </summary>
        /// <param name="monadicFunction">The monadic function.</param>
        /// <remarks>
        /// <para>The Bind operator is central to the function of a monad.  It is used to bind
        /// monads.   It takes a monad (in this case, the 'this' reference) and a reference
        /// to a function (i.e., a delegate) which returns a monad.   A Bind method is
        /// responsible for 'piercing' the first monad in order to access the value it 
        /// contains.   It then passes that value to the monadic function in order to obtain a 
        /// new monad which it returns.</para>
        /// <para>This method elaborates on the basic monadic design.  When it obtains the 
        /// second monad by evaluating the function, it does not depend on external
        /// code to propagate computation through additional calls to 'Bind()'.   Instead, 
        /// the monad is self-propagating.   It enumerates the invocation list of a 
        /// multicast delegate provided by the returned monad.   This delegate has been 
        /// initialised by the called function.  The monad can use multiple delegates in 
        /// the invocation list to model a tree of computational sequences using branching.</para>
        /// <para>Self-propagation is an unusual feature, but is well suited to the needs of the 
        /// rule engine.  Functions are created using generated code specific to a given rule
        /// set.   Computational sequences are identified and analysed by the rule translator.
        /// By encoding these sequences into the generated code for functions passed to monads,
        /// we eliminate any requirement for additional external code to govern those sequences.</para>
        /// </remarks>
        private void Bind(MonadicFunction monadicFunction)
        {
            System.Diagnostics.Debug.WriteLine(((this.innerList.Count > 0) ? this.innerList.First.GetType().Name + "[" + this.innerList.Count.ToString() + "]" : "[0]") + " -> " + monadicFunction.Method.Name);

#if BIND_FUNCTION_PERF
                var startTicks = DateTime.Now.Ticks;
#endif
            var tupleListNew = monadicFunction(this.innerList);
#if BIND_FUNCTION_PERF
                var endTicks = DateTime.Now.Ticks;
                TimeSpan timeSpan;
                if (!TupleList.BindFuncPerf.TryGetValue(monadicFunction.Method.Name, out timeSpan))
                {
                    TupleList.BindFuncPerf.Add(monadicFunction.Method.Name, new TimeSpan());
                }

                TupleList.BindFuncPerf[monadicFunction.Method.Name] += (new TimeSpan(endTicks - startTicks));
#endif
            // hand on the executor as additional context
            tupleListNew.session = this.session;

            if (tupleListNew.quantifiable)
            {
                // The monad is quantifiable, so we need to defer execution until
                // all non-quntifiable monds have been processed.
                this.session.QuantificationQueue.Enqueue(tupleListNew);
            }
            else
            {
                if (tupleListNew.nextFunctions != null)
                {
                    foreach (MonadicFunction bf in tupleListNew.nextFunctions.GetInvocationList())
                    {
                        tupleListNew.Bind(bf);
                    }
                }
            }
        }
    }
}
